﻿using System.Collections.Concurrent;

namespace InfluxDB.Flurl
{
    internal class CacheHelper
    {
        //type cache
        private static readonly ConcurrentDictionary<RuntimeTypeHandle, TypeCache> _cache = [];
        private static readonly object _locker = new();

        internal static TypeCache GetCache(Type t)
        {
            var handle = t.TypeHandle;
            var ok = _cache.TryGetValue(handle, out var val);
            if (!ok)
            {
                lock (_locker)
                {
                    if (!_cache.ContainsKey(handle))
                    {
                        var model = new TypeCache();
                        var pros = t.GetProperties();
                        foreach (var prop in pros)
                        {
                            if (prop.GetCustomAttributes(false).Any(a => a is InfluxIgnoreAttribute))
                            {
                                continue;
                            }

                            //tags
                            if (prop.GetCustomAttributes(false).Any(a => a is InfluxTagAttribute))
                            {
                                if (prop.PropertyType != typeof(string))
                                {
                                    throw new Exception($"{t.FullName} Propertie {prop.Name} Tag must String Type.");
                                }
                                model.TagKeys.Add(prop.Name);
                            }
                            else if (prop.Name == "Time") //time
                            {
                                var type = prop.PropertyType;

                                if (!(type == typeof(DateTimeOffset) || type == typeof(DateTimeOffset?)))
                                {
                                    throw new Exception($"{t.FullName} Propertie Time must DateTimeOffset or DateTimeOffset?");
                                }
                                model.TimeKey = "Time";

                                var pre = (InfluxPrecisionAttribute)prop.GetCustomAttributes(false).FirstOrDefault(f => f is InfluxPrecisionAttribute);
                                if (pre != null)
                                {
                                    model.Precision = pre.Precision;
                                }
                            }
                            else //fields
                            {
                                model.FieldKeys.Add(prop.Name);
                            }
                        }

                        if (model.TagKeys.Count == 0)
                        {
                            throw new Exception($"{t.FullName} no tags,you must add [InfluxTagAttribute] to Properties.");
                        }

                        if (model.FieldKeys.Count == 0)
                        {
                            throw new Exception($"{t.FullName} no fields,you must add field Properties.");
                        }

                        if (string.IsNullOrEmpty(model.TimeKey))
                        {
                            throw new Exception($"{t.FullName} must has Time Propertie,and it is Type must DateTimeOffset or DateTimeOffset? for example [public DateTimeOffset Time....]");
                        }

                        model.TagKeys = [.. model.TagKeys.OrderBy(b => b)];
                        model.FieldKeys = [.. model.FieldKeys.OrderBy(b => b)];

                        _cache.TryAdd(handle, model);
                    }
                }
                val = _cache[handle];
            }
            return val;
        }
    }
}
